package com.eric.study.manager;

import akka.actor.typed.ActorRef;
import akka.actor.typed.Behavior;
import akka.actor.typed.PostStop;
import akka.actor.typed.javadsl.AbstractBehavior;
import akka.actor.typed.javadsl.ActorContext;
import akka.actor.typed.javadsl.Behaviors;
import akka.actor.typed.javadsl.Receive;
import com.eric.study.device.Device;
import com.eric.study.group.DeviceGroup;
import com.eric.study.group.DeviceGroupQuery;
import com.eric.study.message.Message;
import org.slf4j.Logger;

import java.util.*;

/**
 * <p>
 * DeviceManager
 *
 * <p>
 * change history:
 *
 * <pre>
 * date         defect       person          comments
 * --------------------------------------------------------------------------------
 * 2023/8/19    ********     Eric    create file.
 * </pre>
 *
 * @author Eric
 * @date 2023/8/19 15:03
 * @since 1.0.0
 */
public class DeviceManager extends AbstractBehavior<DeviceManager.ManagerMessage> {

    /**
     * 设备管理器械侠管理的所有设备组
     */
    private final Map<String, ActorRef<DeviceGroup.GroupMessage>> groupIdToActor;

    private final Logger log;


    public DeviceManager(ActorContext<ManagerMessage> context) {
        super(context);
        this.groupIdToActor = new HashMap<>();
        this.log = context.getLog();

        log.info("DeviceManager started. path: {}", context.getSelf());
    }

    public static Behavior<ManagerMessage> create() {
        return Behaviors.setup(DeviceManager::new);
    }

    @Override
    public Receive<ManagerMessage> createReceive() {
        return newReceiveBuilder()
                // 接收并处理查询命令
                .onMessage(QueryDeviceList.class, this::onQueryDeviceList)
                // 接收并处理注册命令
                .onMessage(RequestTrackDevice.class, this::onTrackDevice)
                // 接收并处理DeviceGroup Actor 关闭命令
                .onMessage(DeviceGroupTerminated.class, this::onTerminated)
                .onMessage(StopDevice.class, this::stopDevice)
                .onSignal(PostStop.class, signal -> {
                    log.info("DeviceManager stopped.");
                    return this;
                })
                .build();
    }

    private Behavior<ManagerMessage> stopDevice(StopDevice msg) {
        ActorRef<DeviceGroup.GroupMessage> group = groupIdToActor.get(msg.groupId());
        if (Objects.nonNull(group)) {
            group.tell(msg);
        }
        return this;
    }

    private DeviceManager onQueryDeviceList(QueryDeviceList msg) {
        ActorRef<DeviceGroup.GroupMessage> group = groupIdToActor.get(msg.groupId());
        if (Objects.nonNull(group)) {
            group.tell(msg);
        } else {
            msg.replyTo.tell(new ReplyDeviceList(msg.requestId(), Collections.emptySet()));
        }
        return this;
    }

    private DeviceManager onTerminated(DeviceGroupTerminated msg) {
        log.info("Device group actor for {} has been terminated.", msg.groupId);
        groupIdToActor.remove(msg.groupId);
        return this;
    }

    private DeviceManager onTrackDevice(RequestTrackDevice msg) {
        String groupId = msg.groupId();
        ActorRef<DeviceGroup.GroupMessage> group = groupIdToActor.get(groupId);
        if (Objects.nonNull(group)) {
            // Group 存在就转发消息
            group.tell(msg);
        } else {
            // Group 不存在就创建 Group 并转发消息
            log.info("Creating device group actor for [{}]", groupId);

            group = getContext().spawn(DeviceGroup.create(groupId), "DeviceGroup-" + groupId);

            // 观察 Group Actor 状态，如果 Actor 异常停止，则向自己发送一个命令
            getContext().watchWith(group, new DeviceGroupTerminated(groupId));
            groupIdToActor.put(groupId, group);
            group.tell(msg);

        }
        return this;
    }


    // DeviceManager receive message type
    public interface ManagerMessage extends Message {
    }

    // DeviceManager response struct type
    public interface Response {
    }

    // 注册设备请求命令
    public record RequestTrackDevice(String groupId,
                                     String deviceId,
                                     ActorRef<ResponseDeviceRegistered> replyTo) implements ManagerMessage, DeviceGroup.GroupMessage {
    }

    // 注册公共响应消息体
    public record ResponseDeviceRegistered(ActorRef<Device.DeviceMessage> device) implements Response {
    }

    // 查询注册的设备列表命令
    public record QueryDeviceList(long requestId,
                                  String groupId,
                                  ActorRef<ReplyDeviceList> replyTo) implements ManagerMessage, DeviceGroup.GroupMessage {
    }

    // 查询响应消息结构体
    public record ReplyDeviceList(long requestId, Set<String> ids) implements Response {
    }

    /**
     * 关闭设备组命令
     *
     * @param groupId 设备组编号
     */
    private record DeviceGroupTerminated(String groupId) implements ManagerMessage {
    }

    /**
     * 查询所有设备的温度
     *
     * @param requestId 请求编号
     * @param groupId   组编号
     * @param replyTo   返回消息结构体
     */
    public record QueryAllTemperatures(long requestId,
                                       String groupId,
                                       ActorRef<RespondAllTemperatures> replyTo) implements DeviceGroupQuery.GroupQueryMessage, DeviceGroup.GroupMessage, ManagerMessage {
    }

    /**
     * 查询所有设备温度消息响应结果
     *
     * @param requestId    请求编号
     * @param temperatures 所有的温度集合
     */
    public record RespondAllTemperatures(long requestId,
                                         Map<String, TemperatureReading> temperatures) implements Response {
    }

    public interface TemperatureReading {
    }

    /**
     * 具有可用温度的响应结构
     *
     * @param value 温度
     */
    public record Temperature(double value) implements TemperatureReading {
    }

    /**
     * 它已经响应，但还没有可用的温度
     */
    public enum TemperatureNotAvailable implements TemperatureReading {
        INSTANCE
    }

    /**
     * 它在回答之前停止了
     */
    public enum DeviceNotAvailable implements TemperatureReading {
        INSTANCE
    }

    /**
     * 没有在截止日期前回答
     */
    public enum DeviceTimedOut implements TemperatureReading {
        INSTANCE
    }

    /**
     * 模拟关闭指定的设备
     */
    public record StopDevice(long requestId, String groupId,
                             String deviceId) implements ManagerMessage, DeviceGroup.GroupMessage {
    }
}
